// Copyright (c) Microsoft Corporation.  All rights reserved.

#include "stdafx.h"
#include "D3D10Device1.h"

#include "D3D10BlendState1.h"
#include "D3D10Resource.h"
#include "D3D10ShaderResourceView1.h"
#include "DXGI/DXGIAdapter.h"
#include "DXGI/DXGISwapChain.h"

using namespace Microsoft::WindowsAPICodePack::DirectX::Direct3D10;

typedef HRESULT (*D3D10CreateDeviceAndSwapChain1FuncPtr)(
    IDXGIAdapter *pAdapter,
    D3D10_DRIVER_TYPE DriverType,
    HMODULE Software,
    UINT Flags,
    D3D10_FEATURE_LEVEL1 HardwareLevel,
    UINT SDKVersion,
    DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
    IDXGISwapChain **ppSwapChain,    
    ID3D10Device1 **ppDevice
);

typedef HRESULT (*D3D10CreateDevice1FuncPtr)(
    IDXGIAdapter *pAdapter,
    D3D10_DRIVER_TYPE DriverType,
    HMODULE Software,
    UINT Flags,
    D3D10_FEATURE_LEVEL1 HardwareLevel,
    UINT SDKVersion,
    ID3D10Device1 **ppDevice
);

D3DDevice1^ D3DDevice1::CreateDeviceAndSwapChain1(IntPtr windowHandle, [Runtime::InteropServices::Out] SwapChain^ % swapChain)
{
    wchar_t* libName = L"D3D10_1.dll";

    HINSTANCE library = CommonUtils::LoadDll(libName);

    D3D10CreateDeviceAndSwapChain1FuncPtr createFuncPtr = 
        (D3D10CreateDeviceAndSwapChain1FuncPtr) CommonUtils::GetFunctionfromDll(
            library, "D3D10CreateDeviceAndSwapChain1");

    RECT rc;
    GetClientRect( (HWND)windowHandle.ToPointer(), &rc );
    UINT width = rc.right - rc.left;
    UINT height = rc.bottom - rc.top;

#ifdef _DEBUG
    UINT createDeviceFlags = D3D10_CREATE_DEVICE_DEBUG;
#else
    UINT createDeviceFlags = 0;
#endif

    ID3D10Device1* pd3dDevice = NULL;
    IDXGISwapChain* pSwapChain = NULL;

    DXGI_SWAP_CHAIN_DESC sd;

    ZeroMemory( &sd, sizeof( sd ) );
    sd.BufferCount = 1;
    sd.BufferDesc.Width = width;
    sd.BufferDesc.Height = height;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = (HWND)windowHandle.ToPointer();
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;

    array<D3D10_FEATURE_LEVEL1>^ featureLevels = 
    {
        D3D10_FEATURE_LEVEL_10_1,
	    D3D10_FEATURE_LEVEL_10_0,
	    D3D10_FEATURE_LEVEL_9_3,
	    D3D10_FEATURE_LEVEL_9_2,
	    D3D10_FEATURE_LEVEL_9_1
    };

    HRESULT hr = S_OK;
    for each (D3D10_FEATURE_LEVEL1 level in featureLevels)
    {
        hr = (*createFuncPtr)( NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags,
            level, D3D10_1_SDK_VERSION, &sd, &pSwapChain, &pd3dDevice);
        
        if (SUCCEEDED(hr))
        {
            swapChain = gcnew SwapChain(pSwapChain);
            break;
        }
    }

    if (FAILED(hr))
    {
        throw CommonUtils::GetExceptionForHR(hr);
    }

    return gcnew D3DDevice1(pd3dDevice, library);
}

D3DDevice1^ D3DDevice1::CreateDeviceAndSwapChain1(Adapter^ adapter, Microsoft::WindowsAPICodePack::DirectX::Direct3D10::DriverType driverType, IntPtr softwareModule, CreateDeviceFlag flags, FeatureLevel hardwareLevel, SwapChainDescription^ swapChainDescription, [Runtime::InteropServices::Out] SwapChain^ % swapChain)
{
    CommonUtils::CheckNull(swapChainDescription, "swapChainDescription");

    wchar_t* libName = L"D3D10_1.dll";

    HINSTANCE library = CommonUtils::LoadDll(libName);

    D3D10CreateDeviceAndSwapChain1FuncPtr createFuncPtr = 
        (D3D10CreateDeviceAndSwapChain1FuncPtr) CommonUtils::GetFunctionfromDll(
            library, "D3D10CreateDeviceAndSwapChain1");

    ID3D10Device1* pd3dDevice = NULL;
    IDXGISwapChain* pSwapChain = NULL;

    CommonUtils::VerifyResult((*createFuncPtr)( 
        adapter == nullptr ? NULL : adapter->GetInterface<IDXGIAdapter>(), 
        static_cast<D3D10_DRIVER_TYPE>(driverType),
        (HINSTANCE)softwareModule.ToPointer(),
        static_cast<UINT>(flags),
        static_cast<D3D10_FEATURE_LEVEL1>(hardwareLevel),
        D3D10_1_SDK_VERSION, 
        swapChainDescription->nativeObject.Get(), 
        &pSwapChain, 
        &pd3dDevice));

    swapChain = gcnew SwapChain(pSwapChain);

    return gcnew D3DDevice1(pd3dDevice, library);
}

D3DDevice1^ D3DDevice1::CreateDevice1()
{
    wchar_t* libName = L"D3D10_1.dll";

    HINSTANCE library = CommonUtils::LoadDll(libName);

    D3D10CreateDevice1FuncPtr createFuncPtr = 
        (D3D10CreateDevice1FuncPtr) CommonUtils::GetFunctionfromDll(
            library, "D3D10CreateDevice1");

#ifdef _DEBUG
    UINT createDeviceFlags = D3D10_CREATE_DEVICE_DEBUG;
#else
    UINT createDeviceFlags = 0;
#endif

    ID3D10Device1* pd3dDevice = NULL;

    array<D3D10_FEATURE_LEVEL1>^ featureLevels = 
    {
        D3D10_FEATURE_LEVEL_10_1,
	    D3D10_FEATURE_LEVEL_10_0,
	    D3D10_FEATURE_LEVEL_9_3,
	    D3D10_FEATURE_LEVEL_9_2,
	    D3D10_FEATURE_LEVEL_9_1
    };

    HRESULT hr = S_OK;
    for each (D3D10_FEATURE_LEVEL1 level in featureLevels)
    {
        hr = (*createFuncPtr)( NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags,
            level, D3D10_1_SDK_VERSION, &pd3dDevice);

        if (SUCCEEDED(hr))
        {
            break;
        }
    }

    if (FAILED(hr))
    {
        throw CommonUtils::GetExceptionForHR(hr);
    }

    return gcnew D3DDevice1(pd3dDevice, library);
}

D3DDevice1^ D3DDevice1::CreateDevice1(Adapter^ adapter,  Microsoft::WindowsAPICodePack::DirectX::Direct3D10::DriverType driverType, CreateDeviceFlag flags, FeatureLevel hardwareLevel)
{
    wchar_t* libName = L"D3D10_1.dll";

    HINSTANCE library = CommonUtils::LoadDll(libName);

    D3D10CreateDevice1FuncPtr createFuncPtr = 
        (D3D10CreateDevice1FuncPtr) CommonUtils::GetFunctionfromDll(
            library, "D3D10CreateDevice1");

    ID3D10Device1* pd3dDevice = NULL;

    CommonUtils::VerifyResult((*createFuncPtr)( 
        adapter == nullptr ? NULL : adapter->GetInterface<IDXGIAdapter>(), 
        static_cast<D3D10_DRIVER_TYPE>(driverType),
        NULL,
        static_cast<UINT>(flags),
        static_cast<D3D10_FEATURE_LEVEL1>(hardwareLevel),
        D3D10_1_SDK_VERSION, 
        &pd3dDevice));

    return gcnew D3DDevice1(pd3dDevice, library);
}


BlendState1^ D3DDevice1::CreateBlendState1(BlendDescription1^ BlendStateDescription)
{
    ID3D10BlendState1* tempoutBlendState = NULL;
    CommonUtils::VerifyResult(GetInterface<ID3D10Device1>()->CreateBlendState1(
        BlendStateDescription->nativeObject.Get(), &tempoutBlendState));
    return tempoutBlendState == NULL ? nullptr : gcnew BlendState1(tempoutBlendState);
}

ShaderResourceView1^ D3DDevice1::CreateShaderResourceView1(D3DResource^ Resource, ShaderResourceViewDescription1^ Description)
{
    ID3D10ShaderResourceView1* tempoutSRView = NULL;
    CommonUtils::VerifyResult(GetInterface<ID3D10Device1>()->CreateShaderResourceView1(
        Resource->GetInterface<ID3D10Resource>(), Description->nativeObject.Get(), &tempoutSRView));
    return tempoutSRView == NULL ? nullptr : gcnew ShaderResourceView1(tempoutSRView);
}

FeatureLevel D3DDevice1::DeviceFeatureLevel::get()
{
    return static_cast<FeatureLevel>(GetInterface<ID3D10Device1>()->GetFeatureLevel());
}

